﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using Microsoft.Research.DynamicDataDisplay.Charts;

using InfFront.InfoViews.Base;
using InfFront.UICore;

namespace InfFront.InfoViews.WPF
{
    /// <summary>
    /// Interaction logic for DataGridFrame.xaml
    /// </summary>
    public partial class PlotFrame : BaseListWithToolbarContentFrame
    {
        #region Private Variables
        private List<ObservableDataSource<object>> plotDataSources;
        private IAxis defaultVerticalAxis;
        private IAxis defaultHorizontalAxis;

        private static List<string> legendKeys;
        private static DoubleConverter dConverter;
        private static readonly string X_AXIS_LEGEND_KEY = "<X-axis>";
        #endregion

        static PlotFrame()
        {
            legendKeys = new List<string>();
            legendKeys.Add(string.Empty);
            legendKeys.Add(X_AXIS_LEGEND_KEY);
            foreach (var rec in Settings.MappedColorInfoField.EnumItems)
            {
                var val = rec.GetTitle();
                if (val != Settings.MappedColorInfoField.DEFAULT_COLOR_TITLE)
                    legendKeys.Add(val);
            }
            dConverter = new DoubleConverter();
        }

        public PlotFrame()
        {
            Resources.Add("legendKeys", legendKeys);
            plotDataSources = new List<ObservableDataSource<object>>();

            InitializeComponent();
        }

        protected override void DoRebuild()
        {
            plotDataSources.Clear();
            if (legendsList.ItemsSource != null)
                CleanupPlotLegends();

            if (Source == null)
                return;

            var plottableFields = new List<InfoViewField>();
            foreach (var field in Source.Fields)
            {
                var type = field.EffectiveDisplayType;
                if (type.Equals(typeof(double))
                    || type.Equals(typeof(int))
                    || type.Equals(typeof(long))
                    || type.Equals(typeof(float))
                    || type.Equals(typeof(DateTime)))
                {
                    field.VisualSettings.PropertyChanged += fieldVisualSettings_PropertyChanged;
                    plottableFields.Add(field);
                }
            }
            legendsList.ItemsSource = plottableFields;
            RebuildPlot();
        }

        protected override void DoRefresh()
        {
            var records = Source.GetBindingSource();
            foreach (var ds in plotDataSources)
            {
                ds.SuspendUpdate();
                ds.Collection.Clear();
                ds.AppendMany(new EnumerableObjects(records));
                ds.ResumeUpdate();
            }
        }

        private void CleanupPlotLegends()
        {
            InfoViewSource oldSource = null;
            foreach (var fld in (IEnumerable<InfoViewField>)legendsList.ItemsSource)
            {
                fld.VisualSettings.PropertyChanged -= fieldVisualSettings_PropertyChanged;
                oldSource = fld.Owner;
            }
            if (oldSource == null)
                legendsList.ItemsSource = null;
            else
            {
                using (new VisualSettingsFreezeState(oldSource.VisualSettings))
                    legendsList.ItemsSource = null;
            }
        }
        private Func<object, double> GetDateTimeFieldToDouble(InfoViewField fld)
        {
            return obj => xDateTimeAxis.ConvertToDouble
                          (
                              (DateTime)Source.ExtractEntry(obj)
                                              .GetDisplayFieldValue(fld)
                          );
        }

        private Func<object, double> GetNumericFieldToDouble(InfoViewField fld)
        {
            return obj => (double)
                          dConverter.ConvertFromString
                          (
                              Source.ExtractEntry(obj)
                                    .GetDisplayFieldValue(fld)
                                    .ToString()
                          );
        }


        private IAxis DefaultVerticalAxis
        {
            get
            {
                if (defaultVerticalAxis == null)
                    defaultVerticalAxis = plotter.VerticalAxis;
                return defaultVerticalAxis;
            }
        }

        private IAxis DefaultHorizontalAxis
        {
            get
            {
                if (defaultHorizontalAxis == null)
                    defaultHorizontalAxis = plotter.HorizontalAxis;
                return defaultHorizontalAxis;
            }
        }

        private void RebuildPlot()
        {
            plotter.Children.RemoveAll(typeof(LineGraph));
            var records = Source.GetBindingSource();
            var fields = (IEnumerable<InfoViewField>)legendsList.ItemsSource;
            InfoViewField xField = null;
            plotDataSources.Clear();
            int dateTimeLegendCount = 0;
            foreach (var fld in fields)
            {
                var scolor = fld.VisualSettings.GetValue(Settings.Manager.COLOR_PLOT_COLOR_KEY) as string;
                if (string.IsNullOrEmpty(scolor))
                    continue;
                if (scolor == X_AXIS_LEGEND_KEY)
                {
                    xField = fld;
                    continue;
                }
                var color = (Color)ColorConverter.ConvertFromString(scolor);
                // TODO: implement and use custom IPointDataSource here
                var ds = new ObservableDataSource<object>(new EnumerableObjects(records));
                Func<object, double> getY = null;
                if (typeof(DateTime).Equals(fld.EffectiveDisplayType))
                {
                    getY = GetDateTimeFieldToDouble(fld);
                    ++dateTimeLegendCount;
                }
                else
                {
                    getY = GetNumericFieldToDouble(fld);
                }
                ds.SetYMapping(getY);
                plotDataSources.Add(ds);
                plotter.AddLineGraph(ds, color, 2, fld.Title);
            }
            var newVAxis = (plotDataSources.Count != dateTimeLegendCount) ? DefaultVerticalAxis : yDateTimeAxis;
            if (newVAxis != plotter.VerticalAxis)
                plotter.VerticalAxis = newVAxis;
            yDateTimeAxis.Visibility = (dateTimeLegendCount > 0) ? Visibility.Visible : Visibility.Hidden;
            if (xField != null)
            {
                Func<object, double> getX = null;
                if (typeof(DateTime).Equals(xField.EffectiveDisplayType))
                {
                    xDateTimeAxis.Visibility = Visibility.Visible;
                    plotter.HorizontalAxis = xDateTimeAxis;
                    getX = GetDateTimeFieldToDouble(xField);
                }
                else
                {
                    xDateTimeAxis.Visibility = Visibility.Hidden;
                    plotter.HorizontalAxis = DefaultHorizontalAxis;
                    getX = GetNumericFieldToDouble(xField);
                }
                foreach (var ds in plotDataSources)
                    ds.SetXMapping(getX);
            }
        }

        void fieldVisualSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (IsRebuilding)
                return;
            IsRebuilding = true;
            try
            {
                if (e.PropertyName != Settings.Manager.COLOR_PLOT_COLOR_KEY)
                    return;
                var fields = (IEnumerable<InfoViewField>)legendsList.ItemsSource;
                if (fields == null)
                    return;
                var val = (string)((IVisualSettingsAccessor)sender).GetValue(e.PropertyName);
                if (val == X_AXIS_LEGEND_KEY)
                {
                    // erase previous x-axis legends
                    foreach (var fld in fields)
                    {
                        if (fld.VisualSettings != sender
                            && X_AXIS_LEGEND_KEY == (string)fld.VisualSettings.GetValue(e.PropertyName))
                        {
                            fld.VisualSettings.SetValue(e.PropertyName, string.Empty);
                        }
                    }
                }
                RebuildPlot();
            }
            finally
            {
                IsRebuilding = false;
            }
        }

    }
}
